//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "bcm2121.h"
#include "gpio.h"
#include "KeypadTest.h"
#include "ftuart.h"
#include "gsm.h"
#include "bcm_drv.h"
#include "udelay.h"
#include "util.h"

#define MAX_SECTOR_SIZE			0x10000		// for allocate memory
static unsigned int  	sector_buff[MAX_SECTOR_SIZE];


#define FP_StartReq 0x5a		// command to request start transmission
#define FP_StartOK 	0xa5		// command to answer request start transmission
#define FP_ImageOK  0x26		// Write one sector OK
#define FP_SectorOK			(FP_ImageOK+1)			// Write one sector OK
#define FP_Failed			(FP_SectorOK+1)			// Writing failed because unknown reason
#define FP_Finished			(FP_Failed+1)			// all image writing complete
#define FP_Resend			(FP_Finished+1)			// request resend last sector
#define FP_AddrWrong			(FP_Resend+1)			// Start address out of range
#define FP_ImageTooLarge		(FP_AddrWrong+1)		// Image too large compare to the flash space
#define FP_RequestBlockSize 		(FP_ImageTooLarge+1)	// request the sector size
#define FP_ReturnBlockSize		(FP_RequestBlockSize+1)	// retrun the sector size

#define FP_Speed_115k			(0x88)	// use 115k to download
#define FP_Speed_406k			(0x89)	// use 406k to download
#define FP_Speed_806k			(0x90)	// use 806k to download
#define FP_Speed_Req			(0x91)	// request baudrate
#define FP_Speed_ChgDone		(0x92)	// baudrate change done



void bcm2121_on2pc(void)
{
  //OSCC TOUT_EN for 32K HZ CLK bit2
  OSCC |= (0x1<<2);

  udelay(100000);

	xs_setgpio_AFR(34, GPIO_AS_AF1);
 	xs_setgpio_dir(34, GPIO_PIN_IN);

 	xs_setgpio_AFR(37, GPIO_AS_AF3);
 	xs_setgpio_dir(37, GPIO_PIN_IN);

 	xs_setgpio_AFR(26, GPIO_AS_AF3);
 	xs_setgpio_dir(26, GPIO_PIN_IN);

 	xs_setgpio_AFR(27, GPIO_AS_AF3);
 	xs_setgpio_dir(27, GPIO_PIN_IN);

 	xs_setgpio_AFR(39, GPIO_AS_GPIO);
 	xs_setgpio_dir(39, GPIO_PIN_IN);

  xs_setgpio_AFR(22, GPIO_AS_GPIO);
  xs_setgpio_dir(22, GPIO_PIN_OUT);

  xs_setgpio_outlow(22);

  //power on broadcom module
  xs_setgpio_AFR(25, GPIO_AS_GPIO);
  xs_setgpio_dir(25, GPIO_PIN_OUT);

  xs_setgpio_outhigh(25);

  udelay(100000);

  xs_setgpio_AFR(23, GPIO_AS_GPIO);
  xs_setgpio_dir(23, GPIO_PIN_OUT);

  xs_setgpio_outlow(23);

  udelay(300000);

  xs_setgpio_outhigh(23);

  //Wake up BCM2121 for the first time
  xs_setgpio_outlow(114);
  udelay(100000);
  xs_setgpio_outhigh(114);


  	return;
}

void bcm2121_download2pc(void)
{
	xs_setgpio_AFR(34, GPIO_AS_AF1);
 	xs_setgpio_dir(34, GPIO_PIN_IN);

 	xs_setgpio_AFR(37, GPIO_AS_AF3);
 	xs_setgpio_dir(37, GPIO_PIN_IN);

 	xs_setgpio_AFR(26, GPIO_AS_AF3);
 	xs_setgpio_dir(26, GPIO_PIN_IN);

 	xs_setgpio_AFR(27, GPIO_AS_AF3);
 	xs_setgpio_dir(27, GPIO_PIN_IN);

 	xs_setgpio_AFR(39, GPIO_AS_GPIO);
 	xs_setgpio_dir(39, GPIO_PIN_IN);

  	xs_setgpio_AFR(22, GPIO_AS_GPIO);
  	xs_setgpio_dir(22, GPIO_PIN_OUT);

  	xs_setgpio_outhigh(22);

  	xs_setgpio_AFR(23, GPIO_AS_GPIO);
  	xs_setgpio_dir(23, GPIO_PIN_OUT);

  	xs_setgpio_outlow(23);


      	//power on broadcom module
  	xs_setgpio_AFR(25, GPIO_AS_GPIO);
  	xs_setgpio_dir(25, GPIO_PIN_OUT);

  	xs_setgpio_outhigh(25);

  	xs_setgpio_AFR(23, GPIO_AS_GPIO);
  	xs_setgpio_dir(23, GPIO_PIN_OUT);

  	udelay(100);


  	xs_setgpio_outhigh(23);

  	return;
}


static unsigned char GetCheckSum(unsigned char	*buff,unsigned int  sector_size)
{
	unsigned int	i;
	unsigned char	check_sum;

	check_sum = 0;
	for( i=0; i<sector_size; i++ ){
		check_sum += buff[i];
	}
	return( check_sum );
}

#define Image_Start  0xa0000000;
unsigned int i_offset = 0;

static unsigned int  GetNextSector(unsigned char *buff,unsigned int	sector_size)
{
	unsigned int	i;
	char* Imagebuff = (char *)(0xa0000000 + i_offset);
	// clear and read
	for( i=0; i<sector_size; i++ ){
		buff[i] = *(Imagebuff + i);
	}
	i_offset += sector_size;
	return( i_offset);

}


static void SendCurrentSectorAndCheckSum(unsigned char	*buff,unsigned int sector_size)
{
	unsigned int	i;
	unsigned int	j;

	// send whole sector
//	printk( "a new block\n " );

	for( i=0,j=0; i<sector_size; i++,j++ ){
		ftuart_output_rawbyte(buff[i] );
#if 0
		if((i%16)==0)
                {
                        printk("\n\r");
                        printk("%2x",buff[i]);
                }
                else
                {
                        printk("%2x",buff[i]);
                }
#endif
		// spin the cursor
		j &= 0x3fff;
		switch ( j ){
			case 0x000:
				printk( "\b|" );
				break;
			case 0x1000:
				printk( "\b/" );
				break;
			case 0x2000:
				printk( "\b-" );
				break;
			case 0x3000:
				printk( "\b\\" );
				break;
		}
	}
	printk( "\b" );
	printk( "P" );
	printk( "\b" );
	// send check sum
	ftuart_output_rawbyte( GetCheckSum( buff, sector_size ) );
}


int  bcm2121_imagedownload(unsigned int sa, unsigned int is, int speed)
{
	unsigned char ch,ch0,ch1,ch2,ch3;
	int error_status,ret_flag,ss = 0;
	int i;

#if 0
    int j;
	for(j=0; j< is;j++)
        {
        	if((j%16)==0)
                {
                	printk("\n\r");
                        printk("%2x",*(unsigned char*)(0xa0000000 +j));
                }
                else
                {
                        printk("%2x",*(unsigned char*)(0xa0000000 +j));
                }
        }
#endif

	ftuart_gpio_init();
	udelay(100);
	ftuart_init(baud115k2);
	udelay(100);
	bcm2121_gpio_init();
	udelay(100);
	bcm2121_download_enable();
	udelay(100);
	bcm2121_on();
	udelay(1000);

	int length = sizeof(bcm_flashdrv);
	printk("bcm_flashdrv length=%d",length);
	for(i=0; i< length; i++)
	{
		ftuart_output_rawbyte( bcm_flashdrv[i]);
#if 0
		if((i%16)==0)
		{
			printk("\n\r");
			printk("%2x",bcm_flashdrv[i]);
		}
		else
		{
			printk("%2x",bcm_flashdrv[i]);
		}
#endif

	}

	udelay(1000);
	while(!ftuart_input_byte( (char *)&ch));

	if( ch == FP_Speed_Req )
	{
		printk( "\nRECEIVE <- [ Baudrate request ]" );
	}
	else
	{
		printk( "\nOut of sync ch = %d\n",ch );
		return 1;
	}

	switch( speed )
	{
		case 115:
			ch = FP_Speed_115k;
			printk( "\nSEND ----> [ Baudrate = 115k ]" );
			break;

		case 406:
			ch = FP_Speed_406k;
			printk( "\nSEND ----> [ Baudrate = 406k ]" );
			break;

		case 806:
			ch = FP_Speed_806k;
			printk( "\nSEND ----> [ Baudrate = 806k ]" );
			break;


		default:
			printk( "\nWrong Baudrate");
			return 1;
			break;
	}

	ftuart_output_rawbyte( ch );

	udelay(100);
	ftuart_input_byte((char *)&ch);

	if( ch == FP_Speed_ChgDone )
	{
		printk( "\nRECEIVE <- [ Baudrate Change Done ]" );
	}
	else
	{
			printk( "\nOut of sync" );
			return 1;
	}


	ftuart_output_rawbyte( (unsigned char)FP_StartReq );

	error_status = 0;
	ret_flag = 0;

	while(ret_flag == 0 ){

		ret_flag = 0;
		// receive command
		udelay(100);
		ftuart_input_byte( (char *)&ch );

		// switch command
		switch( ch ){
			case FP_StartOK:
				printk( "\nRECEIVE <- [ OK to Start ]" );
				printk( "\nSEND ----> [ Start_address=%08x,  Image_Size=%08x ]",sa,is );
				// send start address
				ftuart_output_rawbyte( (unsigned char) ((sa & 0xff000000) >> 24) );
				ftuart_output_rawbyte( (unsigned char) ((sa & 0x00ff0000) >> 16 ));
				ftuart_output_rawbyte( (unsigned char) ((sa & 0x0000ff00) >> 8 ) );
				ftuart_output_rawbyte( (unsigned char) (sa & 0x000000ff)  );
				// send image size
				ftuart_output_rawbyte( (unsigned char) ((is & 0xff000000) >> 24) );
                                ftuart_output_rawbyte( (unsigned char) ((is & 0x00ff0000) >> 16 ));
                                ftuart_output_rawbyte( (unsigned char) ((is & 0x0000ff00) >> 8 ) );
                                ftuart_output_rawbyte( (unsigned char) (is & 0x000000ff)  );
				break;

			case FP_ImageOK:
				printk( "\nRECEIVE <- [ Image OK! ]" );
				printk( "\nSending " );
				// request sector size
				ftuart_output_rawbyte( (unsigned char)  FP_RequestBlockSize );
				break;

			case FP_AddrWrong:
				printk( "\nRECEIVE <- [ Address Out of Range! ]" );
				error_status = 1;
				ret_flag = 1;
				i_offset = 0;
				break;

			case FP_ImageTooLarge:
				printk( "\nRECEIVE <- [ Image Too Large! ]" );
				error_status = 1;
				ret_flag = 1;
				i_offset = 0;
				break;

			case FP_ReturnBlockSize:
				ftuart_input_byte((char *) &ch3 );
				ftuart_input_byte((char *) &ch2 );
				ftuart_input_byte((char *) &ch1 );
				ftuart_input_byte((char *) &ch0 );
				ss = (ch3 << 24) | (ch2 << 16) | (ch1 << 8) | (ch0);
				// read sector
				if( GetNextSector( (unsigned char*)sector_buff, ss ) == 0 ){
					printk( "\nSector read failed!" );
					error_status = 1;
					ret_flag = 1;
					i_offset = 0;
				}else{
					// send current sector
					SendCurrentSectorAndCheckSum( (unsigned char*)sector_buff, ss );
				}
				break;

			case FP_SectorOK:
				printk( "." );
				// request sector size
				ftuart_output_rawbyte( (unsigned char)FP_RequestBlockSize );
				break;

			case FP_Resend:
				// resend current sector
				printk( "\nRECEIVE <- [ Resend request ]" );
				SendCurrentSectorAndCheckSum( (unsigned char*)sector_buff, ss );
				break;

			case FP_Failed:
				printk( "\nRECEIVE <- [ Transmission Failed ]" );
				error_status = 1;
				ret_flag = 1;
				i_offset = 0;
				break;

			case FP_Finished:
				printk( "\nRECEIVE <- [ Transmission Completely Done ]\n" );
				error_status = 0;
				ret_flag = 1;
				i_offset = 0;
				break;

			default:
				printk( "\nFailed by Unknown Reason( debug_code:%02x ) !\n", ch );
				error_status = 1;
				ret_flag = TRUE;
				i_offset = 0;
				break;
		}


	}
	printk("bcm2121 off\n");
	bcm2121_download_disable();
	bcm2121_off();
	i_offset = 0;
	return error_status;

}


void bcm2121_active(void)
{
  GPSR3 = 0x1 << 18;
  return;
}

void bcm2121_inactive(void)
{
  GPCR3 = 0x1 << 18;
  return;
}
